import type { StoreInfo } from "@/Interface/StoreInfoInterface";

export class StoreDB {
  private dbName = 'StoreDB';
  private storeName = 'stores';
  private version = 2;
  private db: IDBDatabase | null = null;

  // 初始化数据库
  public async initDB(initialData?: StoreInfo[]): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;

        if (!db.objectStoreNames.contains(this.storeName)) {
          const store = db.createObjectStore(this.storeName, {
            keyPath: 'basicInfo.storeId'
          });

          // 添加缺失的金额索引
          store.createIndex('by_city', 'basicInfo.city');
          store.createIndex('by_type', 'basicInfo.storeType');
          store.createIndex('by_openDate', 'basicInfo.openingDate');
          store.createIndex('by_amount', 'annualData.yearAmount');
        }
      };

      request.onsuccess = async (event) => {
        this.db = (event.target as IDBOpenDBRequest).result;

        try {
          if (initialData && (await this.count()) === 0) {
            await this.bulkAdd(initialData);
          }
          resolve();
        } catch (error) {
          reject(error); // 捕获初始化错误
        }
      };

      request.onerror = (event) => {
        reject((event.target as IDBRequest).error);
      };
    });
  }

  // 添加单个门店
  public async add(store: StoreInfo): Promise<void> {
    return this.transaction('readwrite', (storeDB) => {
      return storeDB.put(store);
    });
  }

  // 批量添加
  public async bulkAdd(stores: StoreInfo[]): Promise<void> {
    return this.transaction('readwrite', (storeDB) => {
      stores.forEach(store => storeDB.put(store));
    });
  }

  // 分页查询
  public async getPaged(page: number, pageSize: number): Promise<StoreInfo[]> {
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const storeDB = transaction.objectStore(this.storeName);
      const request = storeDB.openCursor();
      const results: StoreInfo[] = [];
      let counter = 0;
      let advanced = false;

      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;
        if (!cursor) return resolve(results);

        if (!advanced && page > 1) {
          cursor.advance((page - 1) * pageSize);
          advanced = true;
          return;
        }

        if (counter < pageSize) {
          results.push(cursor.value);
          counter++;
          cursor.continue();
        }
      };

      request.onerror = () => reject(request.error);
    });
  }

  // 获取总数
  public async count(): Promise<number> {
    return this.transaction('readonly', (storeDB) => {
      return storeDB.count();
    });
  }

  // 按ID查询
  public async getById(storeId: string): Promise<StoreInfo | undefined> {
    return this.transaction('readonly', (storeDB) => {
      return storeDB.get(storeId);
    });
  }

  // 更新门店
  public async update(storeId: string, newData: Partial<StoreInfo>): Promise<void> {
    const existing = await this.getById(storeId);
    if (!existing) throw new Error('门店不存在');

    return this.transaction('readwrite', (storeDB) => {
      storeDB.put({ ...existing, ...newData });
    });
  }

  // 删除门店
  public async delete(storeId: string): Promise<void> {
    return this.transaction('readwrite', (storeDB) => {
      storeDB.delete(storeId);
    });
  }

  // 统计信息
  public async getStats() {
    const [total, typeData, tagsData, topStores] = await Promise.all([
      this.count(),
      this.aggregateBy('basicInfo.storeType'),
      this.aggregateBy('basicInfo.tags[0]'),
      this.getTopStores(5)
    ]);

    return { total, typeData, tagsData, topStores };
  }

  private async aggregateBy(fieldPath: string): Promise<{ name: string; value: number }[]> {
    const index = fieldPath.replace(/\./g, '_');
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const storeDB = transaction.objectStore(this.storeName);
      const indexDB = storeDB.index(index);
      const aggregator = new Map<string, number>();

      const request = indexDB.openCursor();
      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;
        if (!cursor) {
          resolve(Array.from(aggregator).map(([name, value]) => ({ name, value })));
          return;
        }

        const key = cursor.key.toString();
        aggregator.set(key, (aggregator.get(key) || 0) + 1);
        cursor.continue();
      };

      request.onerror = () => reject(request.error);
    });
  }

  private async getTopStores(limit: number): Promise<StoreInfo[]> {
    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const storeDB = transaction.objectStore(this.storeName);
      const index = storeDB.index('by_amount');
      const request = index.openCursor(null, 'prev');
      const results: StoreInfo[] = [];

      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;
        if (!cursor || results.length >= limit) return resolve(results);

        results.push(cursor.value);
        cursor.continue();
      };

      request.onerror = () => reject(request.error);
    });
  }

  private async transaction<T>(
    mode: IDBTransactionMode,
    action: (store: IDBObjectStore) => IDBRequest | void
  ): Promise<T> {
    if (!this.db) throw new Error('Database not initialized');

    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], mode);
      const storeDB = transaction.objectStore(this.storeName);

      const request = action(storeDB);
      if (!request) return resolve(undefined as unknown as T);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
}

export const storeCRUD = new StoreDB();